A simple example of using inline assembly to perform arithmetic operations like addition.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract AssemblyExample {
function add(uint a, uint b) public pure returns (uint result) {
assembly {
// Inline assembly block
result := add(a, b)
}
}
}
In this example, the assembly block performs an addition using the add opcode of the EVM, where a and b are passed into the inline assembly. The := operator is used to assign the result of the addition.
In this example, we directly access and manipulate storage using inline assembly.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract StorageExample {
uint256 public storedData;
function setStoredData(uint256 x) public {
storedData = x;
}
function getStoredData() public view returns (uint256) {
uint256 result;
assembly {
// Access the first storage slot where `storedData` is located
result := sload(0)
}
return result;
}
}
Here, the sload assembly instruction is used to read from the first storage slot (0), where the state variable storedData is stored.
This example shows how to implement basic conditional logic using inline assembly, such as an if condition.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ConditionalAssembly {
function isEven(uint256 number) public pure returns (bool) {
bool result;
assembly {
// Check if the number is even by performing a modulus operation
// and comparing it to zero.
result := iszero(mod(number, 2))
}
return result;
}
}
Here, the inline assembly checks if the number is even by calculating mod(number, 2) and using the iszero opcode, which returns true if the result is zero (i.e., the number is even).
This example demonstrates how to use loops in inline assembly to compute the factorial of a number.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract LoopAssembly {
function factorial(uint256 n) public pure returns (uint256 result) {
result = 1; // Initial value for factorial computation
assembly {
let i := 1
for { } lt(i, add(n, 1)) { i := add(i, 1) } {
result := mul(result, i)
}
}
}
}
Here, we use an inline assembly for loop to calculate the factorial of a number n. The loop starts from i = 1 and multiplies result by i in each iteration.
This example shows how to call an external contract’s function using inline assembly.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ExternalContract {
function getValue() external pure returns (uint256) {
return 42;
}
}
contract CallExternalAssembly {
function callExternal(address _contract) public view returns (uint256 result) {
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x4d3c70bc) // Function selector for getValue() (first 4 bytes of the keccak256 hash)
let success := staticcall(5000, _contract, ptr, 0x04, ptr, 0x20)
result := mload(ptr) // Load the returned data
}
}
}
In this example, we use staticcall in inline assembly to call the getValue() function of an external contract. We manually specify the function selector for getValue() using its first four bytes from the keccak256 hash.
revertHere’s an example of how to handle errors and revert transactions using inline assembly.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract RevertAssembly {
function checkCondition(uint256 value) public pure returns (string memory) {
assembly {
// If the value is less than 10, revert with an error message.
if lt(value, 10) {
// Error message: "Value too small"
let ptr := mload(0x40)
mstore(ptr, 0x56616c756520746f6f20736d616c6c0000000000000000000000000000000000) // "Value too small" in hex
revert(ptr, 16)
}
}
return "Value is fine";
}
}
In this example, if the input value is less than 10, the transaction reverts with an error message using the revert instruction. The error message “Value too small” is stored in memory and passed to the revert call.
This example demonstrates returning multiple values from inline assembly.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MultipleReturnsAssembly {
function sumAndProduct(uint256 a, uint256 b) public pure returns (uint256 sum, uint256 product) {
assembly {
sum := add(a, b)
product := mul(a, b)
}
}
}
In this case, the assembly block calculates both the sum and product of two numbers and returns them as multiple values.
add, mul, etc., for arithmetic.sload and sstore to read and write directly to storage.staticcall and call.revert to abort execution with custom messages.Title: Layout of State Variables in Storage
Content:
Example:
contract StorageExample {
uint256 a; // Stored at slot 0
uint8 b; // Stored at slot 1 (partially used)
uint8 c; // Stored in slot 1 (shares space with 'b')
uint256 d; // Stored at slot 2
}
Example:
Title: Transient Storage and Stack in Solidity
Content:
Example:
function add(uint a, uint b) public pure returns (uint) {
uint result = a + b; // 'result' is stored in the stack
return result;
}
Note: Stack is cheaper to access than storage but very limited in size.
Title: Layout of Variables in Memory
Content:
Example:
function processData(uint[] memory data) public pure returns (uint) {
return data[0]; // 'data' is stored in memory
}
Memory layout: Arrays are stored contiguously in memory, starting with the length, followed by elements.
Title: Example of Memory Layout with Arrays
Content:
uint[] array, the first slot holds the length, followed by array elements.Diagram:
uint[] memory arr = [1, 2, 3];
[ Length (3) ] [ Element 1 ] [ Element 2 ] [ Element 3 ]
Title: Layout of Call Data
Content:
external.Example:
function setData(uint[] calldata data) external pure returns (uint) {
return data[0]; // 'data' is immutable and passed in call data
}
Call Data Layout:
Title: Cleaning Up Variables
Content:
Example:
function clearData() public {
uint[] storage data = myData;
delete data; // Frees storage space, reducing gas costs
}
Impact on Gas:
Title: Contract Metadata
Content:
Example:
Metadata format:
Title: Contract ABI Specification
Content:
Elements of an ABI:
Example:
[
{
"inputs": [{"name": "x", "type": "uint256"}],
"name": "setData",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
Usage:
Title: Using ABI for Interactions
Content:
Code Example (Web3.js):
const contractABI = [...]; // ABI definition here
const contract = new web3.eth.Contract(contractABI, contractAddress);
// Call a function
contract.methods.setData(123).send({ from: senderAddress });
Explanation: